package image;

import deformedGrid.Distribution;
import stegru.tools.multiThreat.ParallelFor;
import grid.*;

/**
 * 
 * @author Stephan
 *
 */
public class GridBasedImageDeformationOLD {

	final public Grid grid;
	final public int gridSizeX;
	final public int gridSizeY;
	private MorphImage image;

	public GridBasedImageDeformationOLD(final int gridSizeX,
			final int gridSizeY) {
		this.gridSizeX = gridSizeX;
		this.gridSizeY = gridSizeY;
		final double[] yCoords = Distribution.distribute01(gridSizeX);
		final double[] xCoords = Distribution.distribute01(gridSizeY);

		final GridBuilder gBuilder = new GridBuilder(gridSizeX * gridSizeY);

		(new ParallelFor(0, this.gridSizeX - 1) {
			@Override
			protected void loop(int x) {
				for (int y = 0; y < gridSizeY; y++) {
					int glue;
					if (xCoords[x] == 0 || xCoords[x] == 1) {
						if (yCoords[y] == 0 || yCoords[y] == 1) {
							glue = GridPoint.TYPE_GLUE_X_Y;
						} else {
							glue = GridPoint.TYPE_GLUE_X;
						}
					} else if (yCoords[y] == 0 || yCoords[y] == 1) {
						glue = GridPoint.TYPE_GLUE_Y;
					} else {
						glue = GridPoint.TYPE_NON_GLUE;
					}
					gBuilder.set(getGridPointIndex(x, y), new GridPoint(
							xCoords[x], yCoords[y], glue));
				}
			}
		}).calculate();
		this.grid = gBuilder.generate();
	}

	public MorphImage generate(final Grid changedGrid) {
		if (this.image == null) {
			return null;
		}
		// Memory access pattern
		final int imageSizeY = this.image.getImageSizeY();
		final int imageSizeX = this.image.getImageSizeX();
		final int gridSizeX = this.gridSizeX;
		final int gridSizeY = this.gridSizeY;
		final MorphImage image = this.image;
		final double[][] changedGridPoints = changedGrid.toArray();
		final double[][] gridPoints = grid.toArray();
		final double[][][] normalRow = new double[gridSizeX - 1][gridSizeY][3];
		final double[][][] normalColumn = new double[gridSizeX][gridSizeY - 1][3];
		// calculate normalRow
		(new ParallelFor(0, gridSizeY - 1) {
			@Override
			protected void loop(int y) {
				if (y == 0 || y == gridSizeY - 1) {
					for (int x = 0; x < gridSizeX - 1; x++) {
						normalRow[x][y][0] = Double.NaN;
					}
				} else {
					for (int x = 0; x < gridSizeX - 1; x++) {
						double n1 = changedGridPoints[x + 1 + gridSizeX * y][1]
								- changedGridPoints[x + gridSizeX * y][1];
						double n2 = changedGridPoints[x + gridSizeX * y][0]
								- changedGridPoints[x + 1 + gridSizeX * y][0];
						normalRow[x][y][1] = n1;
						normalRow[x][y][2] = n2;
						normalRow[x][y][0] = n1
								* changedGridPoints[x + gridSizeX * y][0] + n2
								* changedGridPoints[x + gridSizeX * y][1];

						// TODO pruefen ob box durch diese seite verlassen
						// werden kann (wenn nein NaN)
					}
				}
			}
		}).calculate();

		// calculate normalColumn
		(new ParallelFor(0, gridSizeX - 1) {
			@Override
			protected void loop(int x) {
				if (x == 0 || x == gridSizeX - 1) {
					for (int y = 0; y < gridSizeY - 1; y++) {
						normalColumn[x][y][0] = Double.NaN;
					}
				} else {
					for (int y = 0; y < gridSizeY - 1; y++) {
						double n1 = changedGridPoints[x + gridSizeX * (y + 1)][1]
								- changedGridPoints[x + gridSizeX * y][1];
						double n2 = changedGridPoints[x + gridSizeX * y][0]
								- changedGridPoints[x + gridSizeX * (y + 1)][0];
						normalColumn[x][y][1] = n1;
						normalColumn[x][y][2] = n2;
						normalColumn[x][y][0] = n1
								* changedGridPoints[x + gridSizeX * y][0] + n2
								* changedGridPoints[x + gridSizeX * y][1];
					}
				}
			}
		}).calculate();

		// Drawing
		(new ParallelFor(0, imageSizeY - 1) {
			@Override
			protected void loop(int y) {
				int gridBoxX = 0;
				int gridBoxY = -1;
				// sucht erste box in der y liegt
				double[] p = new double[2];
				p[1]=y / (imageSizeY - 1.0);
				while (changedGridPoints[gridBoxX + gridSizeX
						* (++gridBoxY + 1)][1] <= p[1]) {
					if (gridBoxY == gridSizeY - 2) { // wenn y in allen
														// davor nicht war
														// muss es in der
														// letzten sein
						break;
					}
				}
				for (int x = 0; x < imageSizeX; x++) {
					p[0] = x / (imageSizeX - 1.0);

					boolean changed;
					int safeCounter = 0;
					do {
						if (safeCounter++ == 1000) {
							System.err.println("infinit loop at box search");
							System.exit(1);
						}
						changed = false;
						/**
						 * p1 ----> p2
						 *  |	    |
						 *  |		|
						 *  \/ 		\/
						 *   p4 ----> p3
						 */
						
						// up
						if (normalRow[gridBoxX][gridBoxY][0] == normalRow[gridBoxX][gridBoxY][0]) { // is
																									// not
																									// NaN
							if (p[0] * normalRow[gridBoxX][gridBoxY][1] + p[1]
									* normalRow[gridBoxX][gridBoxY][2] > normalRow[gridBoxX][gridBoxY][0]) { // true
																												// if
																												// not
																												// in
																												// box
								gridBoxY--;
								changed = true;
							}
						}
						// right
						if (normalColumn[gridBoxX + 1][gridBoxY][0] == normalColumn[gridBoxX + 1][gridBoxY][0]
								&& !changed) { // is not NaN
							if (p[0] * normalColumn[gridBoxX + 1][gridBoxY][1]
									+ p[1]
									* normalColumn[gridBoxX + 1][gridBoxY][2] > normalColumn[gridBoxX + 1][gridBoxY][0]) { // true
																															// if
																															// not
																															// in
																															// box
								gridBoxX++;
								changed = true;
							}
						}
						// down
						if (normalRow[gridBoxX][gridBoxY + 1][0] == normalRow[gridBoxX][gridBoxY + 1][0]
								&& !changed) { // is not NaN
							if (p[0] * normalRow[gridBoxX][gridBoxY + 1][1]
									+ p[1]
									* normalRow[gridBoxX][gridBoxY + 1][2] < normalRow[gridBoxX][gridBoxY + 1][0]) { // true
																														// if
																														// not
																														// in
																														// box
								gridBoxY++;
								changed = true;
							}
						}
						// left
						if (normalColumn[gridBoxX][gridBoxY][0] == normalColumn[gridBoxX][gridBoxY][0]
								&& !changed) { // is not NaN
							if (p[0] * normalColumn[gridBoxX][gridBoxY][1]
									+ p[1]
									* normalColumn[gridBoxX][gridBoxY][2] < normalColumn[gridBoxX][gridBoxY][0]) { // true
																													// if
																													// not
																													// in
																													// box
								gridBoxX--;
								changed = true;
							}
						}
					} while (changed);
					
					double[][] newBox = new double[4][];
					newBox[0] = changedGridPoints[gridBoxX + gridSizeX
							* (gridBoxY)];
					newBox[1] = changedGridPoints[gridBoxX + 1 + gridSizeX
							* (gridBoxY)];
					newBox[2] = changedGridPoints[gridBoxX + 1 + gridSizeX
							* (gridBoxY + 1)];
					newBox[3] = changedGridPoints[gridBoxX + gridSizeX
							* (gridBoxY + 1)];
					double[][] oldBox = new double[4][];
					oldBox[0] = gridPoints[gridBoxX + gridSizeX * (gridBoxY)];
					oldBox[1] = gridPoints[gridBoxX + 1 + gridSizeX
							* (gridBoxY)];
					oldBox[2] = gridPoints[gridBoxX + 1 + gridSizeX
							* (gridBoxY + 1)];
					oldBox[3] = gridPoints[gridBoxX + gridSizeX
							* (gridBoxY + 1)];
					image.draw(x, y, oldBox, newBox);
					//System.out.println("gridBoxX=" +gridBoxX +", gridBoxY=" + gridBoxY);
				}

			}
		}).calculate();

		return this.image;
	}

	public GridPoint getGridPoint(int x, int y) {
		return this.grid.get(x + gridSizeX * y);
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @return x+sizeXCoords*y or -1 if out of bounds
	 * 
	 */
	public int getGridPointIndex(int x, int y) {
		int index = x + gridSizeX * y;
		if (x >= 0 && x < gridSizeX && y >= 0 && y < gridSizeY) {
			return index;
		}
		return -1;
	}

	public MorphImage getImage() {
		return image;
	}


	public void setImage(MorphImage image) {
			this.image = image;
	}

}
